﻿using System.Diagnostics;
using System.Net;
using System.Net.Http.Headers;
using System.Reflection;
using System.Reflection.PortableExecutable;
using System.Text.RegularExpressions;
using System.Web;

namespace Jim.K3Cloud;

/// <summary>
/// 云星空API客户端
/// </summary>
public class K3CloudClient:SdkClient
{
    /// <summary>
    /// 账套Id
    /// </summary>
    public string AcctID { get; set; }
    /// <summary>
    /// 应用
    /// </summary>
    private string AppID { get; set; }
    /// <summary>
    /// 秘钥
    /// </summary>
    private string AppSec { get; set; }
    /// <summary>
    /// 用户
    /// </summary>
    private string UserName { get; set; }
    /// <summary>
    /// 密码
    /// </summary>
    private string Password { get; set;}

    /// <summary>
    /// 多语言
    /// </summary>
    private int Lcid { get; set; } = 2052;
    /// <summary>
    /// 组织编码
    /// </summary>
    private string OrgNum { get; set; }
    /// <summary>
    /// 会话Id
    /// </summary>
    private string SID { get; set; } = ""; 
    /// <summary>
    /// 最后登录时间
    /// </summary>
    private DateTime _sessionTime = new DateTime(1900,1,1);

    /// <summary>
    /// 登录时长
    /// </summary>
    private double DateDif => (DateTime.Now - _sessionTime).TotalMinutes;

    #region 构造函数

    public K3CloudClient(string url, string acctId, string userName, string passsword, int lcid = 2052)
    {
        ServerUrl = url;
        UserName = userName;
        Password = passsword;
        Lcid = lcid;
        init();
    }

    public K3CloudClient(string url, string acctId, string appId, string appSec, string userName, int lcid = 2052)
    {
        ServerUrl = url;
        AcctID = acctId;
        AppID = appId;
        AppSec = appSec;
        UserName = userName;
        Lcid = lcid;
        init();
    }

    public void init()
    {
        httpHelper ??= new HttpHelper();
        CookieContainer ??= new CookieContainer();
    }

    #endregion


    public override async Task<T> Execute<T>(ISdkRequest<T> request)
    {
        if (!request.GetRequestAttr(out SdkAttribute requestAttr))
            return Error<T>($"Input对象缺少SdkAttribute属性，请联系管理员");

        // 登录检查，未登录时需要先预登录
        if (request is not IK3CloudLoginRequest && DateDif >= 175)
        {
            var response = await LoginByAppSecret();
            if(response.IsSuccess && response.Result.LoginResultType == -1) throw new Exception(response.Message);
            else
            {
                this.SID = response.Result.KDSVCSessionId;
                _sessionTime = DateTime.Now;
            }
        }
        if (request is IK3CloudSSORequest<T> ssoRequest)
        {
            // 数据解析
            T ssoRsp = BuildSso<T>(ssoRequest);
            Log($"解析后:{ssoRsp.ToJson()}");
            return ssoRsp;
        }

        try
        {
            // 拼接请求地址
            string url = BuildUrl(request,false, ".common.kdsvc");
            Log($"请求地址:{url}");
            // 请求头信息
            SdkDictionary headers = BuildHeader(request);
            Log($"请求头信息:{headers.ToJson()}");
            // 请求数据
            var postData = BuildPostData(request);
            // 返回数据
            //var json = await url.PostJson(postData, headers);
            HttpItem item = new HttpItem()
            {
              URL = url,
              Method = requestAttr.Method,
              ContentType = "application/json",
              PostDataType = PostDataType.String,
              Postdata =postData,
              CookieContainer = CookieContainer,
              IsUpdateCookie = true,
            };
            var httpResult = await httpHelper.GetHtml(item);
            var json = httpResult.Html;
            Log($"返回信息:{json}");
            T rsp = BuildResponse<T>(request, json);
            Log($"返回对象序列化:{rsp.ToJson()}");
            return rsp;
        }
        catch (Exception e)
        {
            return Error<T>($"服务端请求失败:{e.Message}");
        }
    }



    #region 拼接Header

    private string XApiClientID
    {
        get
        {
            if(string.IsNullOrEmpty(AppID)) return "";
            string str1 = string.Empty;
            int length = AppID.IndexOf("_");
            if(length > -1)
            {
                str1 = AppID.Substring(0, length);
            }
            return str1;
        }
    }

    private string XApiSec
    {
        get
        {
            if(string.IsNullOrEmpty(AppID)) return "";
            var str2 = string.Empty;
            int length = AppID.IndexOf("_");
            if(length > -1)
                str2 = EnDecode.DecryptAppSecret(AppID.Substring(length + 1));
            return str2;
        }
    }

    /// <summary>
    /// 拼接Header
    /// </summary>
    /// <param name="url"></param>
    /// <returns></returns>
    protected override SdkDictionary BuildHeader(ISdkRequest request)
    {
        var headers = base.BuildHeader(request);
        Uri u = new Uri(ServerUrl);
        headers.Add("Context-Type", "application/json");
        var nonce= SdkHelper.GetNonce();
        var timeStamp = SdkHelper.GetTimestamp();

        if(!string.IsNullOrEmpty(XApiClientID))
        {
            headers.Add("X-Api-ClientID", XApiClientID);
            headers.Add("X-Api-Auth-Version", "2.0");
            headers.Add("x-api-signheaders", "x-api-timestamp,x-api-nonce");
            headers.Add("x-api-nonce", nonce);
            headers.Add("x-api-timestamp", timeStamp.ToString());
            string messag= $"POST\n{EnDecode.UrlEncodeWithUpperCode(u.PathAndQuery, Encoding.ASCII)}\n\n\"x-api-nonce:\"{nonce}\n\"x-api-timestamp:\"{timeStamp}\n";
            headers.Add("X-Api-Signature", EnDecode.HmacSHA256(messag, XApiSec, Encoding.ASCII, true));

        }
        headers.Add("X-Kd-Appkey", AppID); 
        string appData = string.Format("{0},{1},{2},{3}", (object)AcctID, (object)UserName, (object)Lcid, (object)OrgNum);
        headers.Add("X-Kd-Appdata", Convert.ToBase64String(Encoding.UTF8.GetBytes(appData)));
        headers.Add("X-Kd-Signature", EnDecode.HmacSHA256(AppID + appData, AppSec, Encoding.UTF8, true));
        if(!string.IsNullOrWhiteSpace(this.SID))
            headers.Add("kdservice-sessionid", this.SID);
        return headers;
    }
    #endregion


    #region 处理Request处理，Post或者自定义SSO
    private T BuildSso<T>(IK3CloudSSORequest<T> request) where T : ISdkResponse
    {
        string url = string.Empty;
        var timeStamp = SdkHelper.GetTimestamp()/1000;
        var userName = string.IsNullOrEmpty(request.UserName) ? UserName: request.UserName;
        var userNameEncod= HttpUtility.UrlEncode(userName, Encoding.UTF8);
        var singArray = new string[] {
            AcctID,
            userName,
            AppID,
            AppSec,
            timeStamp.ToString()
        };
        string sign = singArray.SHA256(true);

        SdkDictionary jobject = new SdkDictionary();
        jobject.Add("appid", AppID);
        jobject.Add("dbid", AcctID);
        jobject.Add("lcid", Lcid.ToString());
        jobject.Add("origintype", request.OriginType);
        jobject.Add("signeddata", sign);
        jobject.Add("timestamp", timeStamp.ToString());
        jobject.Add("username", userName);
        jobject.Add("entryrole", request.EntryRole);
        jobject.Add("formid", request.FormId);
        jobject.Add("formtype", request.FormType);
        jobject.Add("otherargs", request.OtherArgs);
        jobject.Add("pkid", request.PkId);

        string argJosn = jobject.ToJson();
        Log($"请求参数（json格式）：{argJosn}");
        string argJsonBase64 = argJosn.ToBase64();//base64编码
        Log($"参数格式化（Base64）：{argJsonBase64}");

        if(request.UrlType.Equals("SilverLight", StringComparison.CurrentCultureIgnoreCase))
        {
            url = $"{ServerUrl}Silverlight/IndexSL.aspx?ud={argJsonBase64}&udencoding=utf-8";// Silverlight入口链接
        }
        else if(request.UrlType.Equals("HTML", StringComparison.CurrentCultureIgnoreCase))
        {
            url = $"{ServerUrl}html5/Index.aspx?ud={argJsonBase64}&udencoding=utf-8";// html5入口链接
        }
        else
        {
            var k3cloudUrl = ServerUrl.Replace("https://", "K3cloud://");
            url = $"{k3cloudUrl}Clientbin/K3cloudclient/K3cloudclient.manifest?Lcid=2052&ExeType=WPFRUNTIME&LoginUrl={ServerUrl}&ud={argJsonBase64}&udencoding=utf-8"; //客户端入口链接
        }
        Log($"{request.UrlType}单点登录请求地址：{url}");
        return Success<T>(JToken.FromObject(url));
    }

    #endregion

    #region 解析返回值  
    /// <summary>
    /// 返回对象处理
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="value"></param>
    /// <returns></returns>
    protected override T BuildResponse<T>(ISdkRequest request,string value) 
    {
        JToken jToken =null;
        try
        {
            // 尝试进行JSon反序列化
            jToken = JToken.Parse(value);
        }
        catch // 反序列化不成功
        {
            // 用正则判断是否包含json
            Regex regex = new Regex(@"{.*?}$"); // 假设只想提取大括号之间的部分
            Match match = regex.Match(value);
            if(match.Success)//如果包含json，则判断服务器返回错误信息
            {
                string json = match.Value;
                var exception = json.FromJson<K3CloudServiceException>();
                return Error<T>("500", $"云星空服务器内部错误:{exception.Source}/{exception.Message}");
            }
            else
            {
                // 如果不包含json则直接跳过
                return Success<T>(value);
            }
        }

        string message = "";
        if(jToken is JObject jObject)   // 对象
        {
            if(jObject.TryGetValue("Successful", out bool successful, false, true))
            {
                jObject.TryGetValue("ErrorCode", out string vCode, "", true);
                jObject.TryGetValue("Message", out string vMessage, "", true);
                if(!successful)
                {
                    return Error<T>(vCode, vMessage);
                }
                else if(jObject.TryGetValue("Result", out JToken resultToken, null, true))
                {
                    return Success<T>(resultToken);
                }
            }
            else if(jObject.TryGetValue("actionname", out string actionname, null, true))
            {
                if(actionname== "ShowErrMsg")
                {
                    if(jObject.TryGetValue("params", out JToken paramsToken, null, true))
                    {
                        List<K3CloudActionError> errList = paramsToken.ToObject<List<K3CloudActionError>>();
                        if(errList != null)
                        {
                            List<string> errMessageList = new();
                            foreach (var err in errList)
                            {
                                errMessageList.Add($"{err.ErrorTitle}:{err.ErrorInfo}");
                            }
                            message += string.Join(",", errMessageList);
                        }
                        jObject.Remove("params");
                    }
                }
                return Success<T>( message);
            }
            else if(jObject.TryGetValue("Result", out JToken resultToken, null, true)) // 如果包含Result返回值
            {
                if(resultToken.TryGetValue("ResponseStatus", out SdkResponseStatus responseStatus, null, true))// 如果Result-包含ResponseStatus返回值
                {
                    var isSuccess = responseStatus.IsSuccess;
                    var code = responseStatus.ErrorCode.ToString();
                    message = responseStatus.ToString();
                    if(!isSuccess)
                        return Error<T>(code, message);
                }
                if(resultToken.TryGetValue("Result", out JToken resultResultToken, null, true))     // Result里面包Result值，直接返回
                {
                    return Success<T>(resultResultToken);
                }
                else if(resultToken.Count() > 0)    // Result里面包含 非Result值，直接返回
                {
                    return Success<T>(resultToken);
                }
            }
            return Success<T>(jObject);
        }
        else if(jToken is JArray jArray) // 数据对象
        {
            // 单据查询，拼接字段和值
            if(request is K3CloudExecuteBillQueryRequest query)
            {
                List<List<string>> queryResponse = value.FromJson<List<List<string>>>();
                string[] fieldKeys = query.FieldKeys.Split(",");
                List<Dictionary<string, object>> data = new List<Dictionary<string, object>>();
                foreach (List<string> row in queryResponse)
                {
                    Dictionary<string, object> rowDic = new Dictionary<string, object>();
                    for(int i = 0; i < fieldKeys.Length; i++)
                    {
                        var fieldKey = fieldKeys[i];
                        rowDic.Add(fieldKey, row[i]);
                    }
                    data.Add(rowDic);
                }
                return Success<T>(JToken.FromObject(data));
            }
            else   // 非单据查询，直接返回
            {
                return Success<T>(jArray);
            }
        }
        else   // 非JObject和JArray，其他都是基础对象
        {
            return Success<T>(jToken);
        }

    }
    #endregion

    #region Token处理

    public async Task<K3CloudLoginResponse> Login()
    {
        var response = await Execute(new K3CloudValidateUserEnDeCodeRequest
        {
            AcctID = this.AcctID,
            Username = UserName,
            Password = Password,
            Lcid = this.Lcid,
        });
        return response;
    }

    public async Task<K3CloudLoginResponse> LoginByAppSecret()
    {
        var response = await Execute(new K3CloudLoginByAppSecretRequest
        {
            AcctID = this.AcctID,
            AppId = this.AppID,
            AppSecret = this.AppSec,
            Username = this.UserName,
            Lcid = this.Lcid,
        });
        return response;
    }

    #endregion

}
